<?php


namespace App\WorkerMan;

use App\Models\User\BusinessMessage;
use App\Models\User\UserMessage;
use Illuminate\Support\Facades\DB;
use \Workerman\Worker;
use App\Http\Controllers\Auth\JwtToken;
use App\Models\Business\Business;
use App\Models\User\BusinessSocket;
use App\Models\User\UserSocket;
use \GatewayWorker\Lib\Gateway;
use Illuminate\Container\Container;
use Illuminate\Support\Facades\Log;
use Workerman\Lib\Timer;

$rootPath = (dirname(dirname(__DIR__)));
$path = "{$rootPath}/vendor/autoload.php";
require "{$path}";

class Events
{

    //不断遍历数据库 把未确认的信息通过ws发送给前端
    public static function onWorkerStart($businessWorker)
    {


        self::getDb();
        //把所有的session重置
        UserSocket::where(['status' => 1])->update(['status' => 0]);
        BusinessSocket::where(['status'=>1])->update(['status'=>0]);
        //定时广播消消息
        Timer::add(10, [Events::class, 'notify']);

    }

    //数据链接 返回链接成功
    public static function onConnect($client_id)
    {
        echo 112222;
    }

    /**
     * @param $client_id
     * @param $data
     *
     * 链接建立
     *
     *  响应参数(最后将参数作为字典再json化后传递)
     *
     *  type: 3 （消息类型: 1. 成功建立websocket会话  2. 建立失败websocket会话,断开连接）
     */
    public static function onWebSocketConnect($client_id, $data)
    {
        Log::info(2222);
        $data = self::encodeData(1);
        Gateway::sendToClient($client_id, $data);
    }

    /**
     * @param $client_id
     * @param $message
     *
     * 消息格式 dict 传递消息使用json封装
     *
     * 请求参数(最后将参数作为字典再json化后传递)
     *
     *  token: xxxxx (令牌信息,通过登录接口获取 类型:string)
     *  type: 1 （消息类型: 1. 为这个token建立会话 之后有关于这个用户的消息推送会通过ws推送到这个链接上面 3. 确认已经收到推送的消息）
     *  msg_code: 1 (预定义消息类型 -1:收到消息类型错误 前端无法识别 1,2,3,4,5,6 确认收到下面定义好的消息服务端无需再重复推送 类型:int)
     *
     * 响应参数(最后将参数作为字典再json化后传递)
     *
     *  type: 2 （消息类型: 2. 关闭websocket会话 3. 正常消息通知 类型:int）
     *  msg_code: 1 (预定义消息类型 -1:消息类型错误 后台无法识别 0非预定义消息 1,2,3,4,5,6下面定义好的消息类型 200会话建立成功 类型:int)
     *  msg: xxx (如果msg_code为0 那么这个就是预定义的消息类型)
     *
     *
     * msg_code的意义如下
     *
     * 1. 用户下单 通知商家接单
     * 2. 用户退单 通知商家退单消息
     * 3. 商家接单 通知用户接单消息
     * 4. 商家拒收订单 通知用户商家拒收订单
     * 5. 商家开始运送 通知用户商家开始运送
     * 6. 商家完成订单 通知用户商家完成订单
     *
     *
     * 后端流程:
     * 1. 当用户type 1 时
     *     1. 解包json 提取token 验证有效性 有效性失败的 返回错误 要求客户端重新传递
     *     2. 上一步正确的话为user_id和$client_id建立对应关系记录
     *     3.
     */
    public static function onMessage($client_id, $message)
    {

        //验证消息格式
        $msg = self::verifyMsg($message);
        //如果消息类型错误 就告诉客户端
        if (!$msg) {
            $returnMessage = self::encodeData(3, -1);
            Gateway::sendToClient($client_id, $returnMessage);
            return;
        }
        $token = $msg['token'];
        $msgType = $msg['type'];
        //提取token
        $user = JwtToken::verifyToken($token);
        if (!$user) {
            $returnMessage = self::encodeData(3, 400);
            Gateway::sendToClient($client_id, $returnMessage);
            //给用户在数据库中建立会话
            return;
        }

        try {
            $userId = $user['id'];
            $userType = (int)$user['user_type'];
        } catch (\Exception $e) {
            $returnMessage = self::encodeData(3, 401);
            Gateway::sendToClient($client_id, $returnMessage);
            return;
        }

        if ($userType === 1) {
            $socketCls = UserSocket::class;
            $messageCls = UserMessage::class;
        } elseif ($userType === 2) {
            $socketCls = BusinessSocket::class;
            $messageCls = BusinessMessage::class;
        } else {
            $socketCls = null;
            $messageCls =null;
        }
        //开始建立会话
        if ($msgType == 1) {
            //建立会话 把会话信息存入数据库
            if (!$socketCls) {
                return;
            }
            try {
                Log::debug($socketCls);
                $res = $socketCls::updateOrCreate([
                    'client_id' => $client_id,
                ], [
                    'client_id' => $client_id,
                    'user_id' => $userId,
                    'status' => 1
                ]);
                if ($res) {
                    Gateway::sendToClient($client_id, self::encodeData(3, 200));
                } else {
                    Gateway::sendToClient($client_id, self::encodeData(3, 502));
                }
            } catch (\Exception $e) {
                Gateway::sendToClient($client_id, self::encodeData(3, 501));
                return;
            }
        }

        if ($msgType == 3) {
            if($messageCls) {
                //接受用户的确认消息
                try{
                    $id = $msg['id'];
                }catch (\Exception $e){
                    Gateway::sendToClient($client_id, self::encodeData(3, 502));
                    return;
                }
                try {
                    if(!$messageCls::where(['id'=>$id,'user_id'=>$userId])->exists())
                        return;
                    $data = $messageCls::find($id);
                    $data->is_confirm = 1;
                    $data->save();
                    Gateway::sendToClient($client_id, self::encodeData(3, 202));
                }catch (\Exception $e){
                    Log::error("消息确认失败 消息源---{$message}");
                }
            }
        }


    }

    public static function onClose($client_id)
    {
        self::closeSession($client_id);
    }

    public static function closeSession($client_id)
    {
        try {
            UserSocket::where(['client_id' => $client_id])->delete();
        } catch (\Exception $e) {
        }
        try {
            BusinessSocket::where(['client_id' => $client_id])->delete();
        } catch (\Exception $e) {
        }
    }

    /**
     * 广播通知消息逻辑 主要通知订单 退餐等消息
     * 1. 用户下单 通知商家接单
     * 2. 用户退单 通知商家退单消息
     * 3. 商家接单 通知用户接单消息
     * 4. 商家拒收订单 通知用户商家拒收订单
     * 5. 商家开始运送 通知用户商家开始运送
     * 6. 商家完成订单 通知用户商家完成订单
     *
     */
    public static function notify()
    {
        //循环推送未读消息
        $businesses = BusinessMessage::where(['is_confirm'=>0])->with(['session','order'])->get()->toArray();
        $users = UserMessage::where(['is_confirm'=>0])->with(['session','order'])->get()->toArray();

        foreach ($businesses as $business){
            foreach ($business['session'] as $val){
                Gateway::sendToClient($val['client_id'], self::encodeData(3, $business['message_code'],$business['order'],$business['id']));

            }
        }
        foreach ($users as $user){
            foreach ($user['session'] as $val){

                Gateway::sendToClient($val['client_id'], self::encodeData(3, $user['message_code'],$user['order'],$user['id']));
            }
        }
    }

    //返回消息封装格式统一参数
    public static function encodeData($type = 3, $msg_code = 1, $msg = 'xxxxx',$id = 'null')
    {
        $data = array('type' => $type, 'msg_code' => $msg_code, 'msg' => $msg,'id'=>$id);
        return json_encode($data);
    }

    //验证请求消息的格式
    public static function verifyMsg($msg)
    {
        try {
            $data = (array)json_decode($msg);
        } catch (\Exception $e) {
            return false;
        }
        if ($data === null) {
            return false;
        }
        if (sizeof($data) == 0) {
            return false;
        }
        if (!array_key_exists('type', $data) || !array_key_exists('token', $data) || !array_key_exists('msg_code', $data)) {
            return false;
        }
        return $data;
    }

    public static function getDb()
    {
        $settings = array(
            'driver' => 'mysql',
            'host' => 'server.southmeteor.com',
            'database' => 'xjfood',
            'username' => 'xjfood',
            'password' => '8bXpLApJ3my4aLmy',
            'collation' => 'utf8_general_ci',
            'prefix' => '',
            'charset' => 'utf8'
        );
        $connFactory = new \Illuminate\Database\Connectors\ConnectionFactory(new \Illuminate\Container\Container);
        $conn = $connFactory->make($settings);
        $resolver = new \Illuminate\Database\ConnectionResolver();
        $resolver->addConnection('default', $conn);
        $resolver->setDefaultConnection('default');
        \Illuminate\Database\Eloquent\Model::setConnectionResolver($resolver);
    }
}